///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Contains code for homogeneous points.
*	\file		LaborHPoint.h
*	\author		Pierre Terdiman
*	\date		April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABORHPOINT_H__
#define __LABORHPOINT_H__

#include "LaborPoint.h"
#include "LaborCoreDef.h"
NAMESPACE_LABOR3D_BEGIN
class  LABORCORE_API CLHPoint : public CLPoint
{
public:

	//! Empty constructor
	inline				CLHPoint()																		{}
	//! Constructor from floats
	inline				CLHPoint(float _x, float _y, float _z, float _w=0.0f) : CLPoint(_x, _y, _z), w(_w)	{}
	//! Constructor from array
	inline				CLHPoint(const float f[4]) : CLPoint(f), w(f[3])									{}
	//! Constructor from a Point
	inline				CLHPoint(const CLPoint& p, float _w=0.0f) : CLPoint(p), w(_w)						{}
	//! Destructor
	inline				~CLHPoint()																		{}

	//! Clear the point
	inline	CLHPoint&		zero()											{ x =			y =			z =			w = 0.0f;		return *this;	}

	//! Assignment from values
	inline	CLHPoint&		set(float _x, float _y, float _z, float _w )	{ x  = _x;		y  = _y;	z  = _z;	w  = _w;		return *this;	}
	//! Assignment from array
	inline	CLHPoint&		set(const float f[4])							{ x  = f[_X];	y  = f[_Y];	z  = f[_Z];	w  = f[_W];		return *this;	}
	//! Assignment from another h-point
	inline	CLHPoint&		set(const CLHPoint& src)							{ x  = src.x;	y  = src.y;	z  = src.z;	w = src.w;		return *this;	}

	//! Add a vector
	inline	CLHPoint&		add(float _x, float _y, float _z, float _w )	{ x += _x;		y += _y;	z += _z;	w += _w;		return *this;	}
	//! Add a vector
	inline	CLHPoint&		add(const float f[4])							{ x += f[_X];	y += f[_Y];	z += f[_Z];	w += f[_W];		return *this;	}

	//! Subtract a vector
	inline	CLHPoint&		sub(float _x, float _y, float _z, float _w )	{ x -= _x;		y -= _y;	z -= _z;	w -= _w;		return *this;	}
	//! Subtract a vector
	inline	CLHPoint&		sub(const float f[4])							{ x -= f[_X];	y -= f[_Y];	z -= f[_Z];	w -= f[_W];		return *this;	}

	//! Multiplies by a scalar
	inline	CLHPoint&		mul(float s)									{ x *= s;		y *= s;		z *= s;		w *= s;			return *this;	}

	//! Returns MIN(x, y, z, w);
	float		Min()								const		{ return MIN(x, MIN(y, MIN(z, w)));										}
	//! Returns MAX(x, y, z, w);
	float		Max()								const		{ return MAX(x, MAX(y, MAX(z, w)));										}
	//! Sets each element to be componentwise minimum
	CLHPoint&	Min(const CLHPoint& p)							{ x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w);	return *this;	}
	//! Sets each element to be componentwise maximum
	CLHPoint&	Max(const CLHPoint& p)							{ x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w);	return *this;	}

	//! Computes square magnitude
	inline	float		squareMagnitude()					const		{ return x*x + y*y + z*z + w*w;											}
	//! Computes magnitude
	inline	float		magnitude()							const		{ return sqrtf(x*x + y*y + z*z + w*w);									}

	//! Normalize the vector
	inline	CLHPoint&		normalize()
	{
		float M = magnitude();
		if(M)
		{
			M = 1.0f / M;
			x *= M;
			y *= M;
			z *= M;
			w *= M;
		}
		return *this;
	}

	// Arithmetic operators
	//! Operator for HPoint Negate = - HPoint;
	inline	CLHPoint		operator-()							const		{ return CLHPoint(-x, -y, -z, -w);							}

	//! Operator for HPoint Plus  = HPoint + HPoint;
	inline	CLHPoint		operator+(const CLHPoint& p)			const		{ return CLHPoint(x + p.x, y + p.y, z + p.z, w + p.w);		}
	//! Operator for HPoint Minus = HPoint - HPoint;
	inline	CLHPoint		operator-(const CLHPoint& p)			const		{ return CLHPoint(x - p.x, y - p.y, z - p.z, w - p.w);		}

	//! Operator for HPoint Mul   = HPoint * HPoint;
	inline	CLHPoint		operator*(const CLHPoint& p)			const		{ return CLHPoint(x * p.x, y * p.y, z * p.z, w * p.w);		}
	//! Operator for HPoint Scale = HPoint * float;
	inline	CLHPoint		operator*(float s)					const		{ return CLHPoint(x * s, y * s, z * s, w * s);				}
	//! Operator for HPoint Scale = float * HPoint;
	inline	friend	CLHPoint	operator*(float s, const CLHPoint& p)		{ return CLHPoint(s * p.x, s * p.y, s * p.z, s * p.w);		}

	//! Operator for HPoint Div   = HPoint / HPoint;
	inline	CLHPoint		operator/(const CLHPoint& p)			const		{ return CLHPoint(x / p.x, y / p.y, z / p.z, w / p.w);		}
	//! Operator for HPoint Scale = HPoint / float;
	inline	CLHPoint		operator/(float s)					const		{ s = 1.0f / s; return CLHPoint(x * s, y * s, z * s, w * s);	}
	//! Operator for HPoint Scale = float / HPoint;
	inline	friend	CLHPoint	operator/(float s, const CLHPoint& p)		{ return CLHPoint(s / p.x, s / p.y, s / p.z, s / p.w);		}

	//! Operator for float DotProd = HPoint | HPoint;
	inline	float		operator|(const CLHPoint& p)			const		{ return x*p.x + y*p.y + z*p.z + w*p.w;						}
	// No cross-product in 4D

	//! Operator for HPoint += HPoint;
	inline	CLHPoint&		operator+=(const CLHPoint& p)				{ x += p.x; y += p.y; z += p.z; w += p.w;	return *this;	}
	//! Operator for CLHPoint += float;
	inline	CLHPoint&		operator+=(float s)							{ x += s;   y += s;   z += s;   w += s;		return *this;	}

	//! Operator for CLHPoint -= HPoint;
	inline	CLHPoint&		operator-=(const CLHPoint& p)				{ x -= p.x; y -= p.y; z -= p.z; w -= p.w;	return *this;	}
	//! Operator for CLHPoint -= float;
	inline	CLHPoint&		operator-=(float s)							{ x -= s;   y -= s;   z -= s;   w -= s;		return *this;	}

	//! Operator for CLHPoint *= HPoint;
	inline	CLHPoint&		operator*=(const CLHPoint& p)				{ x *= p.x; y *= p.y; z *= p.z; w *= p.w;	return *this;	}
	//! Operator for CLHPoint *= float;
	inline	CLHPoint&		operator*=(float s)							{ x*=s; y*=s; z*=s; w*=s;					return *this;	}

	//! Operator for HPoint /= HPoint;
	inline	CLHPoint&		operator/=(const CLHPoint& p)				{ x /= p.x; y /= p.y; z /= p.z; w /= p.w;	return *this;	}
	//! Operator for HPoint /= float;
	inline	CLHPoint&		operator/=(float s)							{ s = 1.0f / s; x*=s; y*=s; z*=s; w*=s;		return *this;	}

	// Arithmetic operators

	//! Operator for Point Mul = CLHPoint * Matrix3x3;
	CLPoint		operator*(const CLMatrix3x3& mat)		const;
	//! Operator for CLHPoint Mul = CLHPoint * Matrix4x4;
	CLHPoint		operator*(const CLMatrix4x4& mat)		const;

	// CLHPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4
	//! Operator for CLHPoint *= Matrix4x4
	CLHPoint&		operator*=(const CLMatrix4x4& mat);

	// Logical operators

	//! Operator for "if(CLHPoint==CLHPoint)"
	inline	bool		operator==(const CLHPoint& p)			const		{ return ( (x==p.x)&&(y==p.y)&&(z==p.z)&&(w==p.w));			}
	//! Operator for "if(CLHPoint!=CLHPoint)"
	inline	bool		operator!=(const CLHPoint& p)			const		{ return ( (x!=p.x)||(y!=p.y)||(z!=p.z)||(w!=p.w));			}

	// Cast operators

	//! Cast a CLHPoint to a Point. w is discarded.
#ifdef _MSC_VER
	inline				operator	CLPoint()					const		{ return CLPoint(x, y, z);									}
	// gcc complains that conversion to a base class will never use a type conversion operator
#endif

	inline CLHPoint & operator=(const CLHPoint& p)  { x = p.x; y = p.y; z = p.z; w = p.w;	return *this; } 
	inline CLHPoint & operator=(const CLPoint& p)  { x = p.x; y = p.y; z = p.z; 	return *this; } 

public:
	float		w;
};

NAMESPACE_LABOR3D_END

#endif // __LABORHPOINT_H__

